import sys
from itertools import chain

def public(func):
    return func
    
def package(func):

    def wrapper(*params):
        
        self = params[0]
        caller_code = sys._getframe(1).f_code
        
        filename = caller_code.co_filename
        
        if filename != func.func_code.co_filename:
            raise AttributeError('cannot call package visible function {func_name} from {name}'.format(func_name=func.func_name, name=caller_code.co_name))
        return func(*params)
        
    return wrapper

def private(func):
    
    '''
    def wrapper(*params):
        
        list_funcs = lambda self: (f.im_func.func_code for f in filter(lambda f: callable(f) and hasattr(f, 'im_func') and f.__name__ not in object.__dict__.keys(), (self.__getattribute__(name) for name in dir(self))))
        static_funcs = lambda self: (f.func_code for f in filter(lambda f: callable(f) and hasattr(f, 'func_code') and f.__name__ not in object.__dict__.keys(), (self.__getattribute__(name) for name in dir(self))))
        
        self = params[0]
        
        caller_code = sys._getframe(1).f_code
        
        if not caller_code in chain(list_funcs(self), static_funcs(self)):
            raise AttributeError('cannot call private function {func_name} from {name}'.format(func_name=func.__name__, name=caller_code.co_name))
        return func(*params)
        
    return wrapper
    '''
    return func

def protected(func):
    
    '''
    def wrapper(*params):
        
        list_funcs = lambda self: (f.im_func.func_code for f in filter(lambda f: callable(f) and hasattr(f, 'im_func') and f.__name__ not in object.__dict__.keys(), (self.__getattribute__(name) for name in dir(self))))
        static_funcs = lambda self: (f.func_code for f in filter(lambda f: callable(f) and hasattr(f, 'func_code') and f.__name__ not in object.__dict__.keys(), (self.__getattribute__(name) for name in dir(self))))
        
        self = params[0]
        
        caller_code = sys._getframe(1).f_code
        
        if not caller_code in chain(list_funcs(self), static_funcs(self)):
            raise AttributeError('cannot call protected function {func_name} from {name}'.format(func_name=func.func_name, name=caller_code.co_name))
        return func(*params)
        
    return wrapper
    '''
    return func
    
if __name__ == '__main__':

    class T(object):

        @package
        def f(self):
            pass
            
        def fromPublic(self):
            self.f()

    class Singleton(object):

        @private
        def __init__(self):
            #print 'private constructor'
            pass
            
        @staticmethod
        def getInstance():
            return Singleton()
            
    s = Singleton.getInstance()
